/**
 * FileFileBackupDAO.java
 * Created on April 6, 2007, 12:48 PM
 *
 *
 */

package edu.byu.isys413.jjenkins.data;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;

/**
 * summary
 * <p>
 * summary
 *
 * @author      Group 5
 * @version     1.0
 */
public class FileBackupDAO {
    
    /** Creates a new instance of FileFileBackupDAO */
    public FileBackupDAO() {
    }
    /**
     * singleton variable for FileBackupDAO
     */
    private static FileBackupDAO instance = null;
    
    /**
     * This method implements the singleton pattern. It insures that only
     * on instance of InterestDAO exists
     * @return FileBackupDAO
     */
    
    public static synchronized FileBackupDAO getInstance() {
        if (instance == null) {
            instance = new FileBackupDAO();
        }
        return instance;
    }
    
    
    /**
     * This method creates a new FileBackupBO with a unique QUID, puts the interest
     * in cache, and then returns it to the calling method.
     * @throws edu.byu.isys413.jjenkins.data.DataException
     * @return BAckup BO
     */
    
    public FileBackupBO create() throws DataException {
        try {
            
            String id = GUID.generate();
            FileBackupBO object = new FileBackupBO();
            object.setId(id);
            Cache.getInstance().put(object.getId(), object);
            return object;
        } catch (Exception e) {
            throw new DataException("Could not create GUID", e);
        }
        
    }
    
    
    /**
     * This is the public read statement.  It loads an existing record
     * from the database.
     * @param id id of FileBackupBO
     * @throws edu.byu.isys413.jjenkins.data.DataException
     * @return FileBackupBO
     */
    public synchronized FileBackupBO read(String id) throws DataException {
        
        // Checks to see if the item is in cache
        Cache cache = Cache.getInstance();
        FileBackupBO object = (FileBackupBO)cache.get(id);
        try {
            
            // Gets connection
            ConnectionPool connPool = ConnectionPool.getInstance();
            Connection conn = connPool.get();
            
            // If the item is not in cache a connection is checked out and
            // The item is read from the database.
            if(object == null){
                try {
                    //reads in object
                    object = (FileBackupBO)read(id, conn);
                    
                }catch (SQLException e) {
                    conn.rollback();
                    throw new DataException("Could not retrieve record for id=" + id, e);
                }
            }
            
            // connection committs and is returned.
            conn.commit();
            connPool.release(conn);
            
            
            
        } catch (SQLException e) {
            throw new DataException("Could not retrieve record for id=" + id, e);
        } catch (DataException e) {
            throw new DataException("Could not retrieve record for id=" + id, e);
        } catch (ConnectionPoolException e) {
            throw new DataException("Could not retrieve record for id=" + id, e);
        }
        
        // Return object
        return object;
    }
    
    /**
     *  This is a package method that is called by the public read (above) or
     *  by another DAO.  Either way we already have a connection to the database
     *  to use.  The user (controller) code never calls this one directly, since
     *  it can't know about Connection objects or SQLExceptions.
     * @param id id of FileBackupBO
     * @param conn connection
     * @throws java.sql.SQLException
     * @throws edu.byu.isys413.jjenkins.data.DataException
     * @return FileBackupBO
     */
    synchronized FileBackupBO read(String id, Connection conn) throws SQLException, DataException {
        
        
        // Checks to see if the item is in cache
        Cache cache = Cache.getInstance();
        FileBackupBO object = (FileBackupBO)cache.get(id);
        
        // Quieries that database
        if(object == null){
            
            PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM file_backup WHERE backup_id = ?");
            pstmt.setString(1, id);
            pstmt.execute();
            ResultSet rs = pstmt.getResultSet();
            
            
            
            
            
            
            if (rs.next()) {  
                object = new FileBackupBO();
                object.setId(rs.getString("backup_id"));
                object.setFileName(rs.getString("file_name"));
                object.setExtension(rs.getString("extension_id"));
                object.setSizeOfFile(rs.getLong("size_of_file"));
                object.setMemberID(rs.getString("member_id"));
                System.out.println("working:  " + rs.getString("extension_id"));
                object.setIcon((ExtensionDAO.getInstance().readByType(rs.getString("extension_id"),conn)).getIcon());
                object.setInput(rs.getBinaryStream("stored_file"));
                
                
                
                // Sets already in database
                object.setObjectAlreadyInDB(true);
                
                // adds to cache
                cache.put(object.getId(), object);
                
            }else{
                throw new DataException("Object was not found in the database.");
            }
            
        }
        
        
        // returns object
        return object;
    }
    
    
    
    /**
     * This is the public save method.  It is what is called when
     * the user (controller) code wants to save or update an object
     * into the database.
     * @param object FileBackupBO
     * @throws edu.byu.isys413.jjenkins.data.DataException
     */
    public synchronized void save(FileBackupBO object) throws DataException {
        try {
            
                    System.out.println("obtaining connection");
            // retrieve a database connection from the pool
            ConnectionPool connPool = ConnectionPool.getInstance();
            
            Connection conn = connPool.get();
            
            System.out.println("obtained connection");
            try {
                System.out.println("We are about to save");
                // Saves Interest
                save(object, conn);
                
                
            }catch (SQLException e) {
                conn.rollback();
                e.printStackTrace();
                throw new DataException("Could not save record for" + object.getId(), e);
                
            }
            
            // connection committs and is returned.
            conn.commit();
            connPool.release(conn);
            
            // touch the cache for the object
            Cache.getInstance().touch(object.getId());
            
            
            
        } catch (DataException e) {
            throw new DataException("Could not save record for" + object.getId(), e);
        } catch (ConnectionPoolException e) {
            throw new DataException("Could not save record for" + object.getId(), e);
        } catch (SQLException e) {
            throw new DataException("Could not save record for" + object.getId(), e);
        }
        
        
    }
    
    /**
     *  This is a package method that is called by the public save (above) or
     *  by another DAO.  Either way we already have a connection to the database
     *  to use.  The user (controller) code never calls this one directly, since
     *  it can't know about Connection objects or SQLExceptions.
     *
     *  By having other DAOs call this save method (rather than update or
     *  insert below, each DAO in a chained save (like the CustomerDAO calling
     *  MembershipDAO calling InterestDAO save chain) can independently decide
     *  whether to udpate or insert the BO it's saving.  That's why I made
     *  update and insert private rather than package level -- if you call
     *  them directly from another DAO, this DAO can't decide whether it's
     *  object needs to be inserted or updated.
     * @param object FileBackupBO
     * @param conn connection
     * @throws java.sql.SQLException
     * @throws edu.byu.isys413.jjenkins.data.DataException
     */
    synchronized void save(FileBackupBO object, Connection conn) throws SQLException, DataException {
        
        // check the dirty flag in the object.  if it is dirty,
        // run update or insert
        System.out.println("object is not dirty");
        if (object.isDirty()) {
            System.out.println("Object is dirty");
            if (object.isObjectAlreadyInDB()) {
                update(object, conn);
                
            }else{
                insert(object, conn);
            }
            object.setDirty(false);
        }
        
    }
    
    
    /**
     * This method is really a part of save(bo, conn) above.  It could be
     * embedded directly in it, but I've separated it into it's own method
     * to isolate the SQL udpate statement and make it more readable.  But
     * logically, it's really just a part of save.
     *
     * @param FileBackupBO object
     */
    protected synchronized void update(FileBackupBO object, Connection conn) throws SQLException, DataException {
        
        System.out.println("were in the update method");
        PreparedStatement pstmt = conn.prepareStatement("UPDATE file_backup SET backup_id = ?,  file_name = ?, member_id = ?, extension_id = ?, stored_file = ?, size_of_file = ? WHERE backup_id = ?");
        pstmt.setString(1, object.getId());
        pstmt.setString(2, object.getFileName());
        pstmt.setString(3, object.getMemberID());
        pstmt.setString(4, object.getExtension());
        pstmt.setBinaryStream(5, object.getInput(), (int)object.getSizeOfFile());
        pstmt.setLong(6, object.getSizeOfFile());
        pstmt.setString(7, object.getId());
        
        
        pstmt.execute();
        
    }
    
    /**
     * This method is really a part of save(bo, conn) above.  It could be
     * embedded directly in it, but I've separated it into it's own method
     * to isolate the SQL insert statement and make it more readable.  But
     * logically, it's really just a part of save.
     *
     * @param FileBackupBO object
     */
    protected synchronized void insert(FileBackupBO object, Connection conn) throws SQLException, DataException {
        
        System.out.println("we're in the insert method");
        // Creates prepared statemetn
        PreparedStatement pstmt = conn.prepareStatement("INSERT INTO file_backup VALUES(?, ?, ?, ?, ?, ?)");
        pstmt.setString(1, object.getId());;
        pstmt.setString(2, object.getFileName());
        pstmt.setString(3, object.getMemberID());
        pstmt.setString(4, object.getExtension());
        pstmt.setBinaryStream(5, object.getInput(), (int)object.getSizeOfFile());
        pstmt.setLong(6, object.getSizeOfFile());;
        
        
        //Executes prepated statement
        pstmt.execute();
        System.out.println("executed");
        
        //Sets already in database true
        object.setObjectAlreadyInDB(true);
        
    }
    
    /**
     * This method will delete a file transfer. In the future we will jsut make 
     * it archive the ojbect
     * param id 
     * throws DataException
     */
    
    public synchronized void delete(String id)throws DataException{
        try{
        Connection conn = ConnectionPool.getInstance().get();
        System.out.println("got connection");
        PreparedStatement pstmt = conn.prepareStatement("DELETE FROM file_backup WHERE backup_id = ?");
        pstmt.setString(1, id);
        pstmt.execute();
        conn.commit();
        System.out.println("deleted thing");
        ConnectionPool.getInstance().release(conn);
        }catch (ConnectionPoolException e) {
            throw new DataException("Could not save record for", e);
        } catch (SQLException e) {
            throw new DataException("Could not save record for", e);
        }
        
    }
    
    public List readByMember(MemberBO mem)throws DataException{
        
        List files;
        try{
        files = new LinkedList();
        
        // retrieve a database connection from the pool
        ConnectionPool connPool = ConnectionPool.getInstance();
        Connection conn = connPool.get();
        
        
        PreparedStatement pstmt = conn.prepareStatement("SELECT backup_id, file_name, extension_id, size_of_file FROM file_backup WHERE member_id = ?");
        pstmt.setString(1, mem.getId());
        pstmt.execute();
        ResultSet rs = pstmt.getResultSet();
        
        while(rs.next()) {
            ExtensionBO url = ExtensionDAO.getInstance().readByType(rs.getString("extension_id"),conn);
            
            String[] fileinfo = {rs.getString("backup_id"), url.getIcon(),  rs.getString("file_name"), rs.getLong("size_of_file") + ""};
            System.out.println(fileinfo);
            files.add(fileinfo);
            
        }
        conn.commit();
        connPool.release(conn);
        }catch (ConnectionPoolException e) {
            throw new DataException("Could not save record for", e);
        } catch (SQLException e) {
            throw new DataException("Could not save record for", e);
        }
        return files;
    }
    
}
